using System;
using System.ComponentModel;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace OpenComposite.Base
{
	/// <summary>
	/// Summary description for ScrollingListBox.
	/// </summary>
	[ToolboxBitmap(typeof(ListView))]
	public class ScrollingListView : System.Windows.Forms.ListView
	{
		/// <summary> 
		/// Required designer variable.
		/// </summary>
		private System.ComponentModel.Container components = null;

		[Category("Action")]
		public event ScrollEventHandler Scrolled = null;

		private const int WM_HSCROLL = 0x114;
		private const int WM_VSCROLL = 0x115;
		private const int WM_MOUSEWHEEL = 0x20A;
		private const int WM_COMMAND = 0x111;
		private const int WM_USER = 0x400;

		private const int SBS_HORZ = 0;
		private const int SBS_VERT = 1;

		private const int SB_LINELEFT = 0;
		private const int SB_LINERIGHT = 1;
		private const int SB_PAGELEFT = 2;
		private const int SB_PAGERIGHT = 3;
		private const int SB_THUMBPOSITION = 4;
		private const int SB_THUMBTRACK = 5;
		private const int SB_LEFT = 6;
		private const int SB_RIGHT = 7;
		private const int SB_ENDSCROLL = 8;

		private const int SIF_TRACKPOS = 0x10;
		private const int SIF_RANGE = 0x1;
		private const int SIF_POS = 0x4;
		private const int SIF_PAGE = 0x2;
		private const int SIF_ALL = SIF_RANGE | SIF_PAGE | SIF_POS | SIF_TRACKPOS;

		[DllImport("user32.dll", SetLastError=true) ]
		private static extern int GetScrollInfo(IntPtr hWnd, int n, ref ScrollInfoStruct lpScrollInfo );

		[DllImport("user32.dll", SetLastError=true) ]
		private static extern int  SetScrollInfo(IntPtr hwnd, int n, ref ScrollInfoStruct lpcScrollInfo, bool bool_Rename);

		[DllImport("user32")] 
		private static extern int SetScrollPos(IntPtr hwnd, int nBar, int nPos, bool bRedraw);

		[DllImport("user32")] 
		private static extern bool PostMessageA(IntPtr hwnd, int wMsg, int wParam, int lParam);

		[StructLayout(LayoutKind.Sequential)] 
		public struct RECT 
		{ 
			public int left; 
			public int top; 
			public int right; 
			public int bottom; 
	

			public RECT(Rectangle rect) 
			{ 
				this.bottom = rect.Bottom; 
				this.left = rect.Left; 
				this.right = rect.Right; 
				this.top = rect.Top; 
			} 
	

			public RECT(int left, int top, int right, int bottom) 
			{ 
				this.bottom = bottom; 
				this.left = left; 
				this.right = right; 
				this.top = top; 
			}                     
		} 
	
		/// <summary>
		/// Scroll a window.
		/// </summary>
		/// <param name="hWnd">hWnd.</param>
		/// <param name="nXAmount">nXAmount.</param>
		/// <param name="nYAmount">nYAmount.</param>
		/// <param name="rectScrollRegion">rectScrollRegion.</param>
		/// <param name="rectClip">rectClip.</param>
		/// <returns>bool.</returns>
		[DllImport("user32")] 
		public static extern bool ScrollWindow(IntPtr hWnd, int nXAmount, int nYAmount, ref RECT rectScrollRegion, ref RECT rectClip);

		private struct ScrollInfoStruct
		{
			/// <summary />
			public int cbSize;
			/// <summary />
			public int fMask;
			/// <summary />
			public int nMin;
			/// <summary />
			public int nMax;
			/// <summary />
			public int nPage;
			/// <summary />
			public int nPos;
			/// <summary />
			public int nTrackPos;
		}

		/// <summary>
		/// Set the scroll postion.
		/// </summary>
		/// <param name="position">position.</param>
		public void SetScrollPosition(int position)
		{
			ScrollInfoStruct si = new ScrollInfoStruct();
			si.fMask = SIF_ALL;
			si.nPos = position;
			si.cbSize = Marshal.SizeOf(si);

//			SetScrollInfo(this.Handle, 1,ref si, true);
//			SetScrollPos(this.Handle, 1, position, true);
//			PostMessageA(this.Handle, WM_VSCROLL, SB_THUMBPOSITION +0x10000 * position, 1);
//			ScrollToPosition(this.Handle, new Point(position, position));

			RECT r = new RECT(this.ClientRectangle);
			bool a = ScrollWindow(this.Handle,position,position, ref r, ref r);
//			this.Refresh();
		}

		/// <summary>
		/// WndProc.
		/// </summary>
		/// <param name="msg">A Windows message.</param>
		protected override void WndProc(ref System.Windows.Forms.Message msg)
		{
			if( msg.Msg == WM_VSCROLL )
			{
				if( Scrolled != null )
				{
					ScrollInfoStruct si = new ScrollInfoStruct();
					si.fMask = SIF_ALL;
					si.cbSize = Marshal.SizeOf(si);
					GetScrollInfo(msg.HWnd, 1, ref si);

					if( msg.WParam.ToInt32() == SB_ENDSCROLL )
					{
						ScrollEventArgs sargs = new ScrollEventArgs(
							ScrollEventType.EndScroll,
							si.nPos);
						Scrolled(this, sargs);
					}
				}
			}
			base.WndProc(ref msg);
		}
		/// <summary>
		/// Initialize a new instance of ScrollingListView.
		/// </summary>
		public ScrollingListView()
		{
			// This call is required by the Windows.Forms Form Designer.
			InitializeComponent();

			// TODO: Add any initialization after the InitForm call

		}

		/// <summary> 
		/// Clean up any resources being used.
		/// </summary>
		protected override void Dispose( bool disposing )
		{
			if( disposing )
			{
				if(components != null)
				{
					components.Dispose();
				}
			}
			base.Dispose( disposing );
		}

		#region Component Designer generated code
		/// <summary> 
		/// Required method for Designer support - do not modify 
		/// the contents of this method with the code editor.
		/// </summary>
		private void InitializeComponent()
		{
			components = new System.ComponentModel.Container();
		}
		#endregion
	}
}
